package personal.parser;

import personal.util.Map2D;

import java.util.ArrayList;
import java.util.List;

public class SimpleParserTable implements ParserTable {
    @Override
    public int size() {
        return fa.size();
    }

    @Override
    public Grammar getGrammar() {
        return this.grammar;
    }

    @Override
    public ParserFA getParserFA() {
        return this.fa;
    }

    @Override
    public Action getAction(int status, Grammar.Terminal terminal) {
        return actionTable.get(status, terminal);
    }

    @Override
    public int getGoto(int status, Grammar.NonTerminal nonTerminal) {
        return gotoTable.get(status, nonTerminal);
    }


    /**
     * 动作表
     */
//    private final List<Map<Grammar.Symbol, Set<Action>>> actionTable;
    private final Map2D<Integer, Grammar.Symbol, Action> actionTable;

    /**
     * 导致动作的发生的 item
     */
    private final Map2D<Integer, Grammar.Symbol, ParserFA.Item> actionCause;

    /**
     * 转移表
     */
//    private final List<Map<Grammar.Symbol, Integer>> gotoTable;
    private final Map2D<Integer, Grammar.Symbol, Integer> gotoTable;

    /**
     * 文法
     */
    private final Grammar grammar;

    /**
     * 自动机
     */
    private final ParserFA fa;

    private final OperatorTable operatorTable;

    /**
     * 构造函数
     *
     * @param grammar 文法
     */
    public SimpleParserTable(Grammar grammar, OperatorTable operatorTable) {
        // 初始化数据结构
        this.grammar = grammar;
        this.fa = new SimpleParserFA(grammar);
        this.actionTable = new Map2D<>();
        this.actionCause = new Map2D<>();
        this.gotoTable = new Map2D<>();
        this.operatorTable = operatorTable;

        // 开始填表
        for (int idU = 1; idU <= this.size(); ++idU) { // 遍历各个状态
            ParserFA.Status statusU = fa.getStatus(idU);
            // Action
            for (ParserFA.Item item : statusU.Items()) { // 遍历各个 item
                // 如果是终结符，那么填入 ACTION 表
                try { // 移入，即未到达产生式结尾
                    Grammar.Terminal current = (Grammar.Terminal) item.getCurrent(); // 取出当前位置
                    this.putInAction(idU, current, new ShiftAction(fa.transitTo(idU, current)), item); // 那么 Action[idU, symbol] = Sift, goto(idU, symbol)
                } catch (IndexOutOfBoundsException ex) { // 归约，即已经到达产生式末尾，
                    if (item.production().equals(grammar.getProductions().get(0))) { // 如果是第 0 条产生式，
                        this.putInAction(idU, grammar.getSymbol("$"), new AcceptAction(), item); // 那么 Action[idU, $] = accept
                    } else { // 否则，是普通的产生式
                        ReduceAction reduceAction = new ReduceAction(grammar.getProductions().indexOf(item.production()));
                        for (Grammar.Symbol terminalSymbol : item.lookahead()) { // 对于任意 lookahead， Action[idU, lookahead] = Reduce indexOfProduction
                            this.putInAction(idU, terminalSymbol, reduceAction, item);
                        }
                    }
                } catch (ClassCastException ignored) { // 是非终结符，待会儿会填入 GOTO 表
                }
            }
            // Goto
            for (Grammar.NonTerminal nonTerminal : grammar.getNonTerminals()) {
                try { // Goto[idU, A] = v
                    if (this.gotoTable.putIfAbsent(idU, nonTerminal, fa.transitTo(idU, nonTerminal)) != null) {
                        throw new UnknownError("构建 goto 表时，同一位置填入多个项。");
                    }
                } catch (NullPointerException ignored) {
//                    throw new UnknownError("填入 goto 表时，不存在相关转移");
                }

            }
        }
    }



    /**
     * 填入 Action 表，并根据运算符表处理冲突
     *
     * @param u         状态编号
     * @param symbol    终结符
     * @param newAction 动作
     * @param item      导致这次动作的 item
     */
    private void putInAction(int u, Grammar.Symbol symbol, Action newAction, ParserFA.Item item) {
        Action preAction = this.actionTable.putIfAbsent(u, symbol, newAction);
        this.actionCause.putIfAbsent(u, symbol, item);
        if (preAction == null) return;
//        if (this.operatorTable == null) throw new UnsupportedOperationException("暂时未实现解决冲突的逻辑");
        // todo
        if (newAction instanceof AcceptAction) { // 如果新的动作是 acc，那么填入新的动作
            this.actionTable.put(u, symbol, newAction);
            this.actionCause.put(u, symbol, item);
            return;
        }
        if (newAction instanceof ReduceAction && preAction instanceof ReduceAction) { // 如果新的动作是归约，旧的动作是归约
            throw new UnsupportedOperationException("我们假设没有归约-归约冲突，暂时未实现解决冲突的逻辑");
        }
        // 是移入归约冲突
        Grammar.Terminal preOp = this.getOperatorOf(this.actionCause.get(u, symbol)), nowOp = this.getOperatorOf(item);
        int preLevel = this.operatorTable.levelOf(preOp), nowLevel = this.operatorTable.levelOf(nowOp);

        // 如果优先级不相等
        if (preLevel < nowLevel) { // 原来的更高
            return;
        }
        if (preLevel > nowLevel) { // 现在的更高
            this.actionTable.put(u, symbol, newAction);
            this.actionCause.put(u, symbol, item);
            return;
        }
        // 否则优先级相等，判断结合性。左结合使用归约，右结合使用移入
        OperatorTable.Associative associative = this.operatorTable.associativeOf(preOp);
        if (associative.equals(OperatorTable.Associative.RIGHT) && preAction instanceof ShiftAction) return;
        if (associative.equals(OperatorTable.Associative.LEFT) && preAction instanceof ReduceAction) return;
        this.actionTable.put(u, symbol, newAction);
        this.actionCause.put(u, symbol, item);

    }

    /**
     * 获得 item 的产生式的右部的运算符。判断为运算符的依据是是否在运算符表中注册。
     *
     * @param item
     * @return item 的产生式的右部的运算符
     */
    private Grammar.Terminal getOperatorOf(ParserFA.Item item) {
        // 初始化 set
        List<Grammar.Terminal> ret = new ArrayList<>();
        for (Grammar.Symbol symbol : item.production().rightSide()) {
            try {
                Grammar.Terminal terminal = (Grammar.Terminal) symbol;
                if (this.operatorTable.containsKey(terminal)) ret.add(terminal);
            } catch (ClassCastException ignored) {
            }
        }
        // 先处理多个运算符的情况
        if (ret.contains((Grammar.Terminal) this.grammar.getSymbol("("))) { // ( ) sin cos max min
            return (Grammar.Terminal) this.grammar.getSymbol("(");
        }
        if (ret.contains((Grammar.Terminal) this.grammar.getSymbol("?"))) { // ? :
            return (Grammar.Terminal) this.grammar.getSymbol("?");
        }
        // 处理异常情况
        if (ret.size() > 1) throw new UnknownError("产生式右部不应该出现多个运算符");
        return ret.get(0);
    }

    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        for (Grammar.Terminal terminal : this.grammar.getTerminals()) ret.append(terminal).append(", ");
        for (Grammar.NonTerminal nonTerminal : this.grammar.getNonTerminals()) ret.append(nonTerminal).append(", ");
        ret.append("\n");
        for (int u = 1; u <= fa.size(); ++u) {
            ret.append(String.format("%d: ", u));
            for (Grammar.Terminal terminal : this.grammar.getTerminals()) {
                try {
                    ret.append(this.getAction(u, terminal)).append(", ");
                } catch (NullPointerException ex) {
                    ret.append("null, ");
                }
            }
            for (Grammar.NonTerminal nonTerminal : this.grammar.getNonTerminals()) {
                try {
                    ret.append(this.getGoto(u, nonTerminal)).append(", ");
                } catch (NullPointerException ex) {
                    ret.append("null, ");
                }
            }
            ret.append("\n");
        }
        return ret.toString();
    }
}